#pragma once

#include "types.h"

OPEN_JLIB_NS


struct Date {

  constexpr static s64 SecondsPerMinute = 60L;
  constexpr static s64 MinutesPerHour = 60L;
  constexpr static s64 SecondsPerHour = MinutesPerHour * SecondsPerMinute;
  constexpr static s64 HoursPerDay = 24L;
  constexpr static s64 SecondsPerDay = HoursPerDay * SecondsPerHour;
  constexpr static s64 DaysPerWeek = 7L;
  constexpr static s64 SecondsPerWeek = DaysPerWeek * SecondsPerDay;
  constexpr static s64 DaysPerYear = 365L;
  constexpr static s64 SecondsPerYear = DaysPerYear * SecondsPerDay;
  constexpr static s64 DaysPer4Year = DaysPerYear * 4L + 1L;
  constexpr static s64 SecondsPer4Year = DaysPer4Year * SecondsPerDay;
  constexpr static s64 DaysPer100Year = DaysPerYear * 100L + 24L;
  constexpr static s64 SecondsPer100Year = DaysPer100Year * SecondsPerDay;
  constexpr static s64 DaysPer400Year = DaysPerYear * 400L + 97L;
  constexpr static s64 SecondsPer400Year = DaysPer400Year * SecondsPerDay;

  constexpr static s64 MinTimezone = -12;
  constexpr static s64 MaxTimezone = 14;
  constexpr static s64 EpochSecondOffset = (4*DaysPer400Year + 369*DaysPerYear + 89) * SecondsPerDay;
  constexpr static s64 EpochDayOfWeek = 5L;

  static const s32 DaysOfYear[];
  static const s32 DaysOfMonth[];

public:
  static s64 currentSeconds () noexcept;
  static s64 currentMilliseconds () noexcept;
  static s64 currentMicroseconds () noexcept;
  static s64 currentNanoseconds () noexcept;
  static s32 currentTimezone () noexcept;

  Date () noexcept;
  explicit Date (s64 utc) noexcept;
  explicit Date (s64 utc, s32 timezone) noexcept;

  void set (s64 utc) noexcept;
  bool setTime (s32 hour = 0, s32 minute = 0, s32 second = 0) noexcept;
  bool setDate (s32 year, s32 month, s32 day) noexcept;
  bool setDateTime (s32 year, s32 month, s32 day, s32 hour = 0, s32 minute = 0, s32 second = 0) noexcept;
  bool setTimezone (s32 timezone) noexcept;

  Date& addSeconds (s32 value) noexcept;
  Date& addMinutes (s32 value) noexcept;
  Date& addHours (s32 value) noexcept;
  Date& addDays (s32 value) noexcept;
  Date& addWeeks (s32 value) noexcept;
  Date& addMonths (s32 value) noexcept;
  Date& addYears (s32 value) noexcept;

  inline s64 utc () const noexcept { return _u; }
  inline s32 year () const noexcept { return _Y + 1; }
  inline s32 month () const noexcept { return _M + 1; }
  inline s32 day () const noexcept { return _d + 1; }
  inline s32 dayOfYear () const noexcept { return _D + 1; }
  inline s32 dayOfWeek () const noexcept { return _w + 1; }
  inline s32 hour () const noexcept { return _h; }
  inline s32 minute () const noexcept { return _m; }
  inline s32 second () const noexcept { return _s; }
  inline s32 timezone () const noexcept { return _z; }

  inline Date& operator= (s64 utc) noexcept { set(utc); return *this; }

  inline bool operator< (const Date& other) const noexcept { return _u < other._u; }
  inline bool operator> (const Date& other) const noexcept { return _u > other._u; }
  inline bool operator<= (const Date& other) const noexcept { return _u <= other._u; }
  inline bool operator>= (const Date& other) const noexcept { return _u >= other._u; }
  inline bool operator== (const Date& other) const noexcept { return _u == other._u; }
  inline bool operator!= (const Date& other) const noexcept { return _u != other._u; }

private:
  s64 _u; // coordinated universal time
  s64 _l; // local time
  s32 _Y; // year
  s16 _D; // day of year
  s8 _M; // month
  s8 _d; // day of month
  s8 _w; // day of week
  s8 _h; // hour
  s8 _m; // minute
  s8 _s; // second
  s8 _z; // timezone
};


CLOSE_JLIB_NS
